package com.miao.util;

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA加密
 * 非对称加密，有公钥和私钥之分，公钥用于数据加密，私钥用于数据解密。加密结果可逆
 * 公钥一般提供给外部进行使用，私钥需要放置在服务器端保证安全性。
 * 特点：加密安全性很高，但是加密速度较慢
 *
 */
public class RSAUtil {

    private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQk33iNdA8Iey7J6XrBsidqn6u8EDLWPHsfEUgLQ3qiTikhPKDTzZkpAfU/O0x6NvSKa7Dp0+uqWT3vnW1De0+3u8mCYdVfOdH94VG4xg5U5UrRJei8HhPiXuvKQ+6NBtebCCW5adZ4pBgOiU14cJLhVmm+dYiLo3IDD5LqrlomQIDAQAB";

    private static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJCTfeI10Dwh7LsnpesGyJ2qfq7wQMtY8ex8RSAtDeqJOKSE8oNPNmSkB9T87THo29IprsOnT66pZPe+dbUN7T7e7yYJh1V850f3hUbjGDlTlStEl6LweE+Je68pD7o0G15sIJblp1nikGA6JTXhwkuFWab51iIujcgMPkuquWiZAgMBAAECgYA1UT9mciQWWQh9yNRmhXzssFjB2TZ8B5RIe1fe0t7D9NEf0yvAgzDzEo8U3CX5dv/CVL7vxr8bEbt7phCwsa8hJiLEOr7hLZaJzXVTbvfqb91oCZGNkqDQ3NJfGBMVgUmltEYF2Bbk3U0NDyat+Gu54tRd2OH+adJYKsD0XYeDBQJBAN5FE8E04A4FA1q8mQbVTSVJDYIEJwOrdC0r3iZ7za5CyXGk+br8pFalRePFaksRGdN32+mYhDKVNrNHspAObVMCQQCmhBsD+xiWrmpnrzeIfCW1cX8qRC3/RMkq0ACw3l6YedNFdN2Tb5WsRHmcbCI9y8mfLHiG/X1R+zHZKG67EKjjAkAmvAkGSY2mQ89i160fWLq5/bIh71FRPWbgnF15fWfJr4/lgyeWI4MMKn80g2nTrSZACQpE+jRHkGNY+OywWCNLAkEAli5nvztkfeJpDYK2b16pE/B9ZL2BTs3XMcnQFbU5VAPsTKSOgz8MmwZXOIE+kMWP3wPY4McXlC0eVGFnHUh1SQJAeAl3RPk+XbZDMYfPkStRJwocG9Ap+88mwTgR1I7uPzZ1aM84/WsQskiVMXv2SZLmMWvYtnhIKosL6IACp2AcDA==";

    public static void main(String[] args) throws Exception{
        String source = "123456";
        String encode = RSAUtil.encrypt(source);
        System.out.println("encode" + encode);

        String decode = RSAUtil.decrypt(encode);
        System.out.println("decode:" + decode);


    }

    public static String getPublicKeyStr(){
        return PUBLIC_KEY;
    }

    /**
     * 获取RSA公钥对象
     *
     * @return RSAPublicKey 返回解析后的RSA公钥对象
     * @throws Exception 如果解析公钥过程中出现错误，则抛出异常
     */
    public static RSAPublicKey getPublicKey() throws Exception {
        // 将Base64编码的公钥字符串转换为字节数组
        byte[] decoded = Base64.decodeBase64(PUBLIC_KEY);

        // 使用RSA算法的KeyFactory实例化公钥对象
        // 这里使用X509EncodedKeySpec规范是因为公钥通常遵循X.509标准
        return (RSAPublicKey) KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(decoded));
    }


    /**
     * 获取RSA私钥对象
     *
     * @return RSAPrivateKey 返回RSA私钥对象
     * @throws Exception 如果私钥生成过程中发生错误，则抛出异常
     */
    public static RSAPrivateKey getPrivateKey() throws Exception {
        // 将Base64编码的私钥字符串转换为字节数组
        byte[] decoded = Base64.decodeBase64(PRIVATE_KEY);

        // 使用RSA算法生成私钥对象
        // 这里使用PKCS8EncodedKeySpec规范将解码后的字节数组转换为私钥规格对象
        // 然后使用KeyFactory生成私钥对象
        return (RSAPrivateKey) KeyFactory.getInstance("RSA")
                .generatePrivate(new PKCS8EncodedKeySpec(decoded));
    }


    /**
     * 生成RSA密钥对
     *
     * 此方法用于生成RSA算法的密钥对，包括公钥和私钥生成之后，将密钥对对象转换为字符串形式，
     * 以便于存储和传输
     *
     * @return RSAKey对象，包含公钥和私钥及其对应的字符串表示
     * @throws NoSuchAlgorithmException 如果没有提供者支持RSA算法，则抛出此异常
     */
    public static RSAKey generateKeyPair() throws NoSuchAlgorithmException {
        // 实例化密钥对生成器
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器，指定密钥长度和随机源
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成密钥对
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 获取私钥并转换为RSAPrivateKey类型
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 获取公钥并转换为RSAPublicKey类型
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 将公钥编码为字符串形式，便于存储和传输
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        // 将私钥编码为字符串形式，便于存储和传输
        String privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()));
        // 返回包含密钥对信息的RSAKey对象
        return new RSAKey(privateKey, privateKeyString, publicKey, publicKeyString);
    }


    /**
     * 使用RSA公钥加密源字符串
     *
     * @param source 待加密的源字符串
     * @return 加密后的字符串，使用Base64编码
     * @throws Exception 如果加密过程中发生错误，则抛出异常
     */
    public static String encrypt(String source) throws Exception {
        // 解码Base64编码的公钥字符串，获取原始字节流
        byte[] decoded = Base64.decodeBase64(PUBLIC_KEY);

        // 使用解码后的公钥字节流生成RSA公钥对象
        RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(decoded));

        // 创建Cipher对象，用于执行RSA加密操作
        Cipher cipher = Cipher.getInstance("RSA");

        // 初始化Cipher对象，设置操作模式为加密，密钥为之前生成的公钥
        cipher.init(1, rsaPublicKey);

        // 使用Cipher对象对源字符串进行加密，并将加密后的字节流使用Base64编码，转换为字符串返回
        return Base64.encodeBase64String(cipher.doFinal(source.getBytes(StandardCharsets.UTF_8)));
    }


    /**
     * 获取用于RSA加密/解密的Cipher对象
     *
     * @return 初始化后的Cipher对象
     * @throws Exception 如果密钥生成或Cipher初始化过程中发生错误，则抛出异常
     */
    public static Cipher getCipher() throws Exception {
        // 将Base64编码的私钥字符串转换为字节数组
        byte[] decoded = Base64.decodeBase64(PRIVATE_KEY);

        // 使用RSA算法的密钥工厂生成私钥对象
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA")
                .generatePrivate(new PKCS8EncodedKeySpec(decoded));

        // 创建一个RSA算法的Cipher对象
        Cipher cipher = Cipher.getInstance("RSA");

        // 使用私钥初始化Cipher对象为解密模式
        cipher.init(2, rsaPrivateKey);

        // 返回初始化后的Cipher对象
        return cipher;
    }


    /**
     * 解密给定的字符串
     * 使用预定义的Cipher对象对经过Base64编码的字符串进行解密
     *
     * @param text 待解密的字符串，应为Base64编码的字符串
     * @return 解密后的字符串
     * @throws Exception 如果解密过程中发生错误，则抛出异常
     */
    public static String decrypt(String text) throws Exception {
        // 获取Cipher对象用于解密
        Cipher cipher = getCipher();

        // 将输入的字符串从UTF-8字符集转换为字节数组，然后进行Base64解码
        byte[] inputByte = Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8));

        // 使用Cipher对象对字节数组进行解密，并将结果转换为字符串返回
        return new String(cipher.doFinal(inputByte));
    }


    public static class RSAKey {
        private RSAPrivateKey privateKey;
        private String privateKeyString;
        private RSAPublicKey publicKey;
        public String publicKeyString;

        public RSAKey(RSAPrivateKey privateKey, String privateKeyString, RSAPublicKey publicKey, String publicKeyString) {
            this.privateKey = privateKey;
            this.privateKeyString = privateKeyString;
            this.publicKey = publicKey;
            this.publicKeyString = publicKeyString;
        }

        public RSAPrivateKey getPrivateKey() {
            return this.privateKey;
        }

        public void setPrivateKey(RSAPrivateKey privateKey) {
            this.privateKey = privateKey;
        }

        public String getPrivateKeyString() {
            return this.privateKeyString;
        }

        public void setPrivateKeyString(String privateKeyString) {
            this.privateKeyString = privateKeyString;
        }

        public RSAPublicKey getPublicKey() {
            return this.publicKey;
        }

        public void setPublicKey(RSAPublicKey publicKey) {
            this.publicKey = publicKey;
        }

        public String getPublicKeyString() {
            return this.publicKeyString;
        }

        public void setPublicKeyString(String publicKeyString) {
            this.publicKeyString = publicKeyString;
        }
    }

}